import type { AxiosHeaders, AxiosRequestConfig, AxiosInstance } from 'axios'
import { http, type HttpResponse } from './http'
import { cloneDeep, merge } from 'lodash-es'

const apiModules = import.meta.glob<false, string, Record<string, AxiosRequestConfig>>(
  '../api/*.ts',
  {
    import: 'default',
    eager: false
  }
)
// cache api modules, there maybe issues during vite hot reload
const cacheModules = new Map<string, Record<string, AxiosRequestConfig>>()

type NotError<T = any> = [null, T, AxiosHeaders]
type HasError = [Error, null, null]
type SendRequestOptions = {
  module: string
  api: string
  override?: AxiosRequestConfig
  axiosInstance?: AxiosInstance
  uri?: Record<string, string | number>
}

/**
 *
 * @description
 * const [error, data] = await sendRequest<HttpResponse<unknown[]>>({
 *    module: "user",
 *    api: "info",
 *    axiosInstance: http,
 *    override: {
 *      method: "post",
 *      data: {...}
 *    },
 *    uri: {
 *      id: '1'
 *    }
 * });
 *
 */
export async function sendRequest<D = HttpResponse>({
  module,
  api,
  override,
  axiosInstance = http,
  uri
}: SendRequestOptions): Promise<NotError<D> | HasError> {
  if (!cacheModules.has(module)) {
    const apiModuleAsync = apiModules[`../api/${module}.ts`]
    if (!apiModuleAsync) {
      return [new Error('api module not found'), null, null]
    }

    const apiRequestConfigs = await apiModuleAsync()
    if (!apiRequestConfigs) {
      return [new Error('api request configs not found'), null, null]
    }

    cacheModules.set(module, apiRequestConfigs)
  }

  const apiRequestConfig = cacheModules.get(module)?.[api]
  if (!apiRequestConfig) {
    return [new Error('api request config not found'), null, null]
  }

  try {
    const config = cloneDeep(apiRequestConfig)
    if (config.url?.includes(':')) {
      config.url = formatUrlByUri(config.url, uri || {})
    }
    const { data, headers } = await axiosInstance(override ? merge(config, override) : config)
    return [null, data, headers as AxiosHeaders]
  } catch (error: any) {
    return [error, null, null]
  }
}

function formatUrlByUri(url: string, uri: Record<string, string | number>) {
  return url.replace(/:([^/]+)/g, (_, key) => uri[key] as string)
}
